<template>
  <div class="wrap">
    <!--分类函数N1-->
<!--    <div class="d-flex align-items-center">-->
<!--      <label class="el-form-item__label">N1:</label>-->
<!--      <el-select v-model="params.valueN1">-->
<!--        <el-option-->
<!--          v-for="item in classParamsN1"-->
<!--          :key="item.value"-->
<!--          :label="item.text"-->
<!--          :value="item.value">-->
<!--        </el-option>-->
<!--      </el-select>-->
<!--      <label class="label">   </label>-->
<!--      <label class="el-form-item__label">    N2:</label>-->
<!--      <el-select v-model="params.valueN2">-->
<!--        <el-option-->
<!--          v-for="item in classParamsN2"-->
<!--          :key="item.value"-->
<!--          :label="item.text"-->
<!--          :value="item.value">-->
<!--        </el-option>-->
<!--      </el-select>-->
<!--    </div>-->

    <el-tabs  @tab-click="tabClick">
      <el-tab-pane :label="$t('message.WingProfileDesignDlg.paramDesign')">
        <!--最大厚度-->
        <div class="d-flex align-items-center">
          <label class="el-form-item__label">{{$t('message.WingProfileDesignDlg.maxThickness')}}</label>
          <el-input id="maxThickness"
                    v-model="params.maxThickness"
                    type = "number"
                    step = "10"
                    min = "10"
                    @focus="focus('maxThickness')"
                    @change="changeValue('maxThickness')"/>
          <label class="label">mm</label>
        </div>
        <!--最大弯度-->
        <div class="d-flex align-items-center">
          <label class="el-form-item__label">{{$t('message.WingProfileDesignDlg.maxCamber')}}</label>
          <el-input id="maxCamber"
                    v-model="params.maxCamber"
                    :disabled="params.checkedSymmetry"
                    type = "number"
                    step = "10"
                    min = "0"
                    @focus="focus('maxCamber')"
                    @change="changeValue('maxCamber')"/>
          <label class="label">mm</label>
        </div>
        <!--最大弯度位置-->
        <div class="d-flex align-items-center">
          <label class="el-form-item__label">{{$t('message.WingProfileDesignDlg.maxCamberPos')}}</label>
          <el-input id="maxCamberPos"
                    v-model="params.maxCamberPos"
                    :disabled="params.checkedSymmetry"
                    type = "number"
                    step = "1"
                    min = "10"
                    max = "90"
                    @focus="focus('maxCamberPos')"
                    @change="changeValue('maxCamberPos')"/>
          <label class="label">%</label>
        </div>
        <!--旋转角度-->
        <div class="d-flex align-items-center">
          <label class="el-form-item__label">{{$t('message.WingProfileDesignDlg.rotateAngle')}}</label>
          <el-input id="rotateAngle"
                    v-model="params.rotateAngle"
                    :disabled="params.checkedSymmetry"
                    type = "number"
                    step = "1"
                    @focus="focus('rotateAngle')"
                    @change="changeValue('rotateAngle')"/>
          <label class="label">°</label>
        </div>
        <!--后缘厚度-->
        <div class="d-flex align-items-center">
          <label class="el-form-item__label">{{$t('message.WingProfileDesignDlg.trailingEdgeThickness')}}</label>
          <el-input id="trailingEdgeThickness"
                    v-model="params.trailingEdgeThickness"
                    :disabled="params.checkedSymmetry"
                    type = "number"
                    step = "1"
                    min = "-90"
                    max = "90"
                    @focus="focus('trailingEdgeThickness')"
                    @change="changeValue('trailingEdgeThickness')"/>
          <label class="label">%</label>
        </div>
        <!--U方向偏移-->
        <div class="d-flex align-items-center">
          <label class="el-form-item__label">{{$t('message.WingProfileDesignDlg.dirUOffset')}}</label>
          <el-input id="dirUOffset"
                    v-model="params.dirUOffset"
                    type = "number"
                    step = "1"
                    @focus="focus('dirUOffset')"
                    @change="changeValue('dirUOffset')"/>
          <label class="label">mm</label>
        </div>
        <!--V方向偏移-->
        <div class="d-flex align-items-center">
          <label class="el-form-item__label">{{$t('message.WingProfileDesignDlg.dirVOffset')}}</label>
          <el-input id="dirVOffset"
                    v-model="params.dirVOffset"
                    type = "number"
                    step = "1"
                    @focus="focus('dirVOffset')"
                    @change="changeValue('dirVOffset')"/>
          <label class="label">mm</label>
        </div>
        <!--前缘半径-->
        <div class="d-flex align-items-center">
          <label class="el-form-item__label">{{$t('message.WingProfileDesignDlg.leadingEdgeRadius')}}</label>
          <el-input id="leadingEdgeRadius"
                    v-model="params.leadingEdgeRadius"
                    :disabled="true"
                    type = "number"
                    step = "10"
                    min = "100"
                    @focus="focus('leadingEdgeRadius')"
                    @change="changeValue('leadingEdgeRadius')"/>
          <label class="label">mm</label>
        </div>
        <!--后缘角度-->
        <div class="d-flex align-items-center">
          <label class="el-form-item__label">{{$t('message.WingProfileDesignDlg.trailingEdgeAngle')}}</label>
          <el-input id="trailingEdgeAngle"
                    v-model="params.trailingEdgeAngle"
                    :disabled="true"
                    type = "number"
                    step = "1"
                    min = "1"
                    @focus="focus('trailingEdgeAngle')"
                    @change="changeValue('trailingEdgeAngle')"/>
          <label class="label">°</label>
        </div>
        <div class="d-flex mt-2 align-items-center">
          <el-checkbox v-model="params.checkedSymmetry">{{$t('message.WingProfileDesignDlg.symmetryWingProfile')}}</el-checkbox>
        </div>

      </el-tab-pane>
      <el-tab-pane :label="$t('message.WingProfileDesignDlg.standardDesign')">
        <label class="el-form-item__label">{{$t('message.WingProfileDesignDlg.standardNACAFour')}}</label>
        <div class="d-flex align-items-center">
          <label class="el-form-item__label">NACA</label>
          <el-input id="standardNACAFour"
                    @focus="focus('standardNACAFour')"
                    @change="changeValue('standardNACAFour')"
                    v-model="params.standardNACAFour"/>
        </div>

      </el-tab-pane>
    </el-tabs>

    <!--弦长-->
    <div class="d-flex align-items-center" v-if="params.curTab === 'TabParam'">
      <label class="el-form-item__label">{{$t('message.WingProfileDesignDlg.chordLength')}}</label>
      <el-input id="chordLength"
                v-model="params.chordLength"
                type = "number"
                step = "10"
                min = "100"
                @focus="focus('chordLength')"
                @change="changeValue('chordLength')"/>
      <label class="label">mm</label>
    </div>
    <div class="btn">
      <el-button
        type="primary"
        size="small"
        :disabled="false"
        @click="confirm">
        {{ $t('message.WingProfileDesignDlg.confirm') }}
      </el-button>
      <el-button
        type="primary"
        size="small"
        :disabled="false"
        @click="exportTxt">
        {{ $t('message.WingProfileDesignDlg.exportTxt') }}
      </el-button>
      <el-button
        size="small"
        @click="close">
        {{ $t('message.WingProfileDesignDlg.cancel') }}
      </el-button>
    </div>
  </div>
</template>

<script>
import DialogMixin from "../DialogMixin";
import {wingProfileDesignCommand} from "../../../Command/WingProfileDesign/WingProfileDesignCommand";
import {sqrt} from "three/nodes";

export default {
  mixins: [DialogMixin],
  data: function() {
    return {
      params: {

        curTab: "TabParam",
        chordLength:3000,             //弦长
        leadingEdgeRadius:0,          //前缘半径
        maxCamber:300,                //最大弯度
        maxCamberPos:50,              //最大弯度位置，采用百分比
        trailingEdgeAngle:20,         //后缘角度
        maxThickness:450,             //最大厚度
        valueN1:0.5,                  //N1值
        valueN2:1,                    //N2值
        trailingEdgeThickness:0,      //后缘厚度
        dirUOffset:0,                 //在U方向上的偏置值
        dirVOffset:0,                 //在V方向上的偏置值
        rotateAngle:0,                //翼型的旋转角度，弧度
        checkedSymmetry:false,        //是否为对称翼型，对称翼型时，最大弯度、最大弯度位置均默认调整为0


        //在进行对称之前的参数进行记录
        lastMaxCamber:0,              //上次的最大弯度
        lastMaxCamberPos:0,           //上次的最大弯度位置
        lastTrailingEdgeThickness:0,  //上次的后缘厚度
        lastRotateAngle:0,            //上次的旋转角度

        standardNACAFour:"2415",      //默认的NACA型号
      },

      classParamsN1:[
        {text:"0.25", value: 0.25},
        {text:"0.5", value: 0.5},
        {text:"0.75", value: 0.75},
        {text:"1.0", value: 1.0},
      ],
      classParamsN2:[
        {text:"0.25", value: 0.25},
        {text:"0.5", value: 0.5},
        {text:"0.75", value: 0.75},
        {text:"1.0", value: 1.0},
      ],
      //drawPoints:[],                  //绘制翼型的控制点
      drawPointsUp:[],                //绘制翼型的上控制点
      drawPointsDown:[],              //绘制翼型的下控制点
      samplePointsUp:[],              //上半部分曲线采样点
      samplePointsDown:[],            //下半部分曲线采样点
      listControlPointsUp:[],         //上半部分控制点
      listControlPointsDown:[],       //下半部分控制点
      orderNumber:5,                  //使用的拟合方程的阶数
      formulaParamUp:{},              //上曲线的参数
      formulaParamDown:{},            //下曲线的参数
      originValue: 0,
    };
  },

  mounted() {
    //进行初始化之前，应该先让用户选择一个平面作为草图的基准面
    //设置当前命令时已经处理。
    wingProfileDesignCommand.init(400,300,270,() => {
      this.preview();
    });
  },

  methods: {
    changePluginContainerHeight() {
      wingProfileDesignCommand.changePluginContainerHeight({minWidth: this.$el.clientWidth, maxHeight: this.$el.clientHeight});
    },
    focus(name) {
      this.originValue = this.params[name];
    },
    changeValue(name) {
      let element = document.getElementById(name);
      if (element) {
        let minValue = Number(element.min);
        let maxValue = Number(element.max);
        let value = this.params[name];
        if ((maxValue <= 0 && value < minValue) || (maxValue > 0 && (value < minValue || value > maxValue))) {
          this.params[name] = this.originValue;
        }
      }
    },
    tabClick(tab,event){
      if(tab.index === "0"){
        this.params.curTab = "TabParam";
        this.params.chordLength = 3000;
      }
      else{
        this.params.curTab = "TabNACA";
        this.params.chordLength = 1000;
      }
    },
    confirm: function() {
      let params = {
        pickPntListUp:this.drawPointsUp,
        pickPntListDown:this.drawPointsDown,
        isPreview: false,
        isDrawPoints: false,
      };

      //进行样条曲线绘制
      wingProfileDesignCommand.drawSketch(params);
      wingProfileDesignCommand.closeActiveCmd();
      //this.close();
    },
    //导出txt文档
    exportTxt(){
      //获取当前草图的旋转矩阵
      wingProfileDesignCommand.getSketchModeMatrix(matrix => {
        //记录Matrix
        let strLinePoints = "Matrix:"
        strLinePoints += "\n";
        for(let i = 0;i < matrix.length;i++){
          let matrixElement = matrix[i].toString();
          if(i !== 0){
            strLinePoints += " ";
          }
          strLinePoints += matrixElement;
        }

        strLinePoints += "\n";
        //当前导出文件，使用上曲线下曲线的方式来区分上下曲线翼型面
        strLinePoints += "Up Curve:";
        this.drawPointsUp.forEach(point => {
          let x = (point.x / this.params.chordLength).toFixed(4);
          let y = (point.y / this.params.chordLength).toFixed(4);
          let z = (point.z / this.params.chordLength).toFixed(4);
          let strPoint = x.toString() + " " + y.toString() + " " + z.toString();
          strLinePoints += "\n" + strPoint;
        })
        strLinePoints += "\n" + "Down Curve:";
        this.drawPointsDown.forEach(point => {
          let x = (point.x / this.params.chordLength).toFixed(4);
          let y = (point.y / this.params.chordLength).toFixed(4);
          let z = (point.z / this.params.chordLength).toFixed(4);
          let strPoint = x.toString() + " " + y.toString() + " " + z.toString();
          strLinePoints += "\n" + strPoint;
        })
        wingProfileDesignCommand.exportTxt(strLinePoints,this.$t('message.WingProfileDesignDlg.exportTxt') + '.txt');
      });
    },

    close() {
      wingProfileDesignCommand.exitSketch(res => {
        wingProfileDesignCommand.closeActiveCmd();
      });
    },

    preview(){
      //清理所有列表
      this.setAllParam();

      this.calculateFitPoint();
    },
    //进行拟合点计算
    calculateFitPoint(){
      //获取输入翼型草图中的对应的点集
      this.getWingProfileCoord();

      //利用坐标点，使用插值方法得到控制点坐标
      this.getControlPoints();

      //通过控制点坐标，求解上下曲线方程的参数
      this.formulaParamUp = this.calculateSplineParam(this.listControlPointsUp);
      this.formulaParamDown = this.calculateSplineParam(this.listControlPointsDown);

      if (!this.formulaParamUp || !this.formulaParamDown) {
        return;
      }

      //对生成的曲线进行检查，如果上曲线-下曲线获取到的点在(0,c)上存在负数，不应该绘制当前曲线，并进行报错
      let bIntersect = this.getIntersectUpAndDownSpline(this.formulaParamUp,this.formulaParamDown);
      if(bIntersect){
        //存在冲突，进行提示并返回
        wingProfileDesignCommand.eventBusAlertMsg("所选参数存在上下曲线交叉冲突","warning");
        return;
      }

      //函数求导
      //一次求导
      let dyUp0 = this.getFunctionDerivativeOnX(this.formulaParamUp,0);
      let dyDown0 = this.getFunctionDerivativeOnX(this.formulaParamDown,0);
      let dyUp1 = this.getFunctionDerivativeOnX(this.formulaParamUp,this.params.chordLength);
      let dyDown1 = this.getFunctionDerivativeOnX(this.formulaParamDown,this.params.chordLength);

      //获取二级导数
      let dy2Up0 = this.getFunctionTwoDerivativeOnX(this.formulaParamUp,0);
      let dy2Down0 = this.getFunctionTwoDerivativeOnX(this.formulaParamDown,0);

      //曲率半径
      let dCurvatureRadiusUp = (1 + dyUp0 ** 2) ** 1.5 / Math.abs(dy2Up0);
      let dCurvatureRadiusDown = (1 + dyDown0 ** 2) ** 1.5 / Math.abs(dy2Down0);

      //前缘半径
      let leadingEdgeRadius;
      if(dCurvatureRadiusUp > dCurvatureRadiusDown){
        leadingEdgeRadius = dCurvatureRadiusUp;
      }else{
        leadingEdgeRadius = dCurvatureRadiusDown;
      }
      this.params.leadingEdgeRadius = Math.round(leadingEdgeRadius * 100) / 100;
      //后缘角度
      this.params.trailingEdgeAngle = Math.round((Math.atan(Math.abs(dyUp1)) + Math.atan(Math.abs(dyDown1))) * 180 / Math.PI * 100) / 100;

      //分别求出来的翼型上下曲线控制点
      let pointsUp = this.calculateSplintPoints(this.formulaParamUp,60,1);
      let pointsDown = this.calculateSplintPoints(this.formulaParamDown,60,1);

      this.drawPointsUp = pointsUp;
      this.drawPointsDown = pointsDown;
      // //上下曲线用一条曲线绘制，由上曲线的终点->上曲线起点->下曲线终点
      // for(let n = pointsUp.length - 1; n > 0; n--){
      //   this.drawPoints.push(pointsUp[n]);
      // }
      // for(let n = 0;n < pointsDown.length;n++){
      //   this.drawPoints.push(pointsDown[n]);
      // }
      if(this.params.curTab === "TabParam"){
        //点绕某个点进行旋转、平移一定的角度
        this.drawPointsUp = this.transformPoints(this.drawPointsUp,this.params.rotateAngle * Math.PI / 180,new THREE.Vector3(this.params.dirUOffset / 1,this.params.dirVOffset / 1),new THREE.Vector3());
        this.drawPointsDown = this.transformPoints(this.drawPointsDown,this.params.rotateAngle * Math.PI / 180,new THREE.Vector3(this.params.dirUOffset / 1,this.params.dirVOffset / 1),new THREE.Vector3());
      }

      let params = {
        pickPntListUp:this.drawPointsUp,
        pickPntListDown:this.drawPointsDown,
        isPreview:true,
        isDrawPoints:false,    //test：是否对样条线上的点进行绘制，用于进行正确性验证
      }

      //调用产品、内核的方法根据控制点生成样条曲线，并进行闭合点的切角修改
      wingProfileDesignCommand.drawSketch(params);
    },

    //根据最大厚度/弦长/最大弯度/最大弯度位置获取对应的采样点
    getWingProfileCoord(){
      //设定的参数值
      let chordLength = this.params.chordLength;                                            //弦长
      let maxThickness = this.params.maxThickness;                                          //最大厚度
      let maxCamberPos = this.params.maxCamberPos / 100;                                    //最大弯度所在位置 * 100
      let maxCamber = this.params.maxCamber / chordLength;                                  //最大弯度

      //如果是NACA设计模式下，使用四位数表达法获取对应的最大厚度、最大弯度位置、最大弯度
      if(this.params.curTab === "TabNACA"){
        //判断四位数机制，并处理获取对应参数
        if(!this.judgeNACAValueValid(this.params.standardNACAFour)){
            return;
        }

        let paramsNACA = this.getNACANumbers(this.params.standardNACAFour);
        maxCamber = paramsNACA[0] / 100;
        maxCamberPos = paramsNACA[1] / 10;
        maxThickness = paramsNACA[2] / 100 * this.params.chordLength;
      }

      let sampleStep = 100;                                                                   //采样点的数量，仅上部分或下部分

      //定义一个基础翼型公式
      //let a = 0.2948;//0.2969;参考文献原公式进行修正
      let a = 0.2969;
      let b = 0.126;
      let c = 0.3516;
      let d = 0.2843;
      let e = 0.1015;

      //在基础公式上进行计算
      let x = 0;
      let yt = 0;

      let testUp = [];
      while(x < chordLength){
        let f = a * (x / chordLength) ** 0.5 -
          b * x / chordLength -
          c * (x / chordLength) ** 2 +
          d * (x / chordLength) ** 3 -
          e * (x / chordLength) ** 4;
        yt = maxThickness  / 0.2 * f;
        if(yt < 0){
          break;
        }

        //中弧线上的点
        let yh = 0;
        let dyt = 0;
        if(x <= chordLength * maxCamberPos){
          yh = maxCamber * x / (maxCamberPos ** 2) * (2 * maxCamberPos - x / chordLength);
          dyt = maxCamber * (2 / maxCamberPos - 2 / (chordLength * maxCamberPos ** 2) * x);
        }else{
          yh = maxCamber * (chordLength - x) / ((1 - maxCamberPos) ** 2) * (1 + x / chordLength - 2 * maxCamberPos);
          dyt = maxCamber * 2 * (chordLength * maxCamberPos - x) / (chordLength * (1 - maxCamberPos) ** 2);
        }
        testUp.push(new THREE.Vector3(x,yh));

        //在导函数曲线上某点的反正切三角函数θ
        let tangentAngle = Math.atan(dyt);

        //计算上下坐标取样点xu,yu,xd,yd
        let xu = x - yt * Math.sin(tangentAngle);
        let yu = yh + yt * Math.cos(tangentAngle);
        let xd = x + yt * Math.sin(tangentAngle);
        let yd = yh - yt * Math.cos(tangentAngle);

        //如果x的值小于零，认为根据中弧线进行的翼型上下曲线计算失败，应该进行错误提示并停止生成
        // if(xu < 0 || xd < 0){
        //   debugger;
        // }
        //将上下部分的采样点都存储到列表中
        if(xu >=0){
          this.samplePointsUp.push(new THREE.Vector3(xu,yu));
        }
        if(xd >= 0){
          this.samplePointsDown.push(new THREE.Vector3(xd,yd));
        }

        x += chordLength / sampleStep ;
      }

      //在上下的坐标点中，因为基础公式中在x = chordLength时y != 0，因此，在点中加入末端点，保证闭合
      this.samplePointsUp.push(new THREE.Vector3(chordLength,0));
      this.samplePointsDown.push(new THREE.Vector3(chordLength,0));
    },

    //利用坐标点，使用插值方法得到控制点坐标
    getControlPoints(){
      //获取所要获取的控制点的数量,通过N1、N2均匀地在曲线上边取点
      let nNo = 0;
      let controlPointsX = [];         //控制点的X值
      let nControlDenominator = this.params.valueN1 + this.params.valueN2 + this.orderNumber;    //N1 + N2 + n 作为控制点的分母
      while(nNo <= this.orderNumber){
        controlPointsX.push((this.params.valueN1 + nNo) / nControlDenominator * this.params.chordLength);
        nNo++;
      }

      let nControlPointNoUp = 0;
      let nSamplePointNoUp = 0;
      //采用三次插值算法
      while(nSamplePointNoUp < this.samplePointsUp.length && nControlPointNoUp < controlPointsX.length){
        if(this.samplePointsUp[nSamplePointNoUp].x > controlPointsX[nControlPointNoUp]){
          //对于当前的控制点，如果采样点的x大于控制点的x，取控制点左右各三个采样点，通过三次插值对中间值进行计算
          //如果控制点的左侧或者右侧的采样点数量比较少，不应该进行插值计算，三次插值两边都应该至少有两个点
          if(nSamplePointNoUp < 2 || nSamplePointNoUp > this.samplePointsUp.length - 2){
            break;
          }

          //计算控制点用到的插值点的x值
          let listSamplePoint = [this.samplePointsUp[nSamplePointNoUp - 2],
            this.samplePointsUp[nSamplePointNoUp - 1],
            this.samplePointsUp[nSamplePointNoUp],
            this.samplePointsUp[nSamplePointNoUp + 1]];

          //计算插值点
          let dInterpolation = this.getInsertPointY(listSamplePoint,controlPointsX[nControlPointNoUp]);
          this.listControlPointsUp.push(new THREE.Vector3(controlPointsX[nControlPointNoUp],dInterpolation));

          //如果所有控制点已经计算完毕，跳出循环
          nControlPointNoUp++;
          if(nControlPointNoUp + 1 > controlPointsX.length){
            break;
          }
        }
        nSamplePointNoUp++;
      }

      let nControlPointNoDown = 0;
      let nSamplePointNoDown = 0;
      //采用三次插值算法
      while(nSamplePointNoDown < this.samplePointsDown.length && nControlPointNoDown < controlPointsX.length){
        if(this.samplePointsDown[nSamplePointNoDown].x > controlPointsX[nControlPointNoDown]){
          //对于当前的控制点，如果采样点的x大于控制点的x，取控制点左右各三个采样点，通过三次插值对中间值进行计算
          //如果控制点的左侧或者右侧的采样点数量比较少，不应该进行插值计算，三次插值两边都应该至少有两个点
          if(nSamplePointNoDown < 2 || nSamplePointNoDown > this.samplePointsDown.length - 2){
            break;
          }

          //计算控制点用到的插值点的x值
          let listSamplePoint = [this.samplePointsDown[nSamplePointNoDown - 2],
            this.samplePointsDown[nSamplePointNoDown - 1],
            this.samplePointsDown[nSamplePointNoDown],
            this.samplePointsDown[nSamplePointNoDown + 1]];

          //计算插值点
          let dInterpolation = this.getInsertPointY(listSamplePoint,controlPointsX[nControlPointNoDown]);
          this.listControlPointsDown.push(new THREE.Vector3(controlPointsX[nControlPointNoDown],dInterpolation));

          //如果所有控制点已经计算完毕，跳出循环
          nControlPointNoDown++;
          if(nControlPointNoDown + 1 > controlPointsX.length){
            break;
          }
        }
        nSamplePointNoDown++;
      }
    },

    //根据采样点坐标和控制点的X值，求控制点的坐标
    getInsertPointY(listSamplePoint,pointXControl){
      if(listSamplePoint.length != 4){
        return;
      }

      //方程组矩阵
      let matrix = [
        [listSamplePoint[0].x ** 3,listSamplePoint[0].x ** 2,listSamplePoint[0].x,1],
        [listSamplePoint[1].x ** 3,listSamplePoint[1].x ** 2,listSamplePoint[1].x,1],
        [listSamplePoint[2].x ** 3,listSamplePoint[2].x ** 2,listSamplePoint[2].x,1],
        [listSamplePoint[3].x ** 3,listSamplePoint[3].x ** 2,listSamplePoint[3].x,1]
      ]

      //矩阵求逆
      let invMatrix = this.inv(matrix);

      //获取三次方程的abcd参数
      let a = invMatrix[0][0] * listSamplePoint[0].y +
        invMatrix[0][1] * listSamplePoint[1].y +
        invMatrix[0][2] * listSamplePoint[2].y +
        invMatrix[0][3] * listSamplePoint[3].y;
      let b = invMatrix[1][0] * listSamplePoint[0].y +
        invMatrix[1][1] * listSamplePoint[1].y +
        invMatrix[1][2] * listSamplePoint[2].y +
        invMatrix[1][3] * listSamplePoint[3].y;
      let c = invMatrix[2][0] * listSamplePoint[0].y +
        invMatrix[2][1] * listSamplePoint[1].y +
        invMatrix[2][2] * listSamplePoint[2].y +
        invMatrix[2][3] * listSamplePoint[3].y;
      let d = invMatrix[3][0] * listSamplePoint[0].y +
        invMatrix[3][1] * listSamplePoint[1].y +
        invMatrix[3][2] * listSamplePoint[2].y +
        invMatrix[3][3] * listSamplePoint[3].y;

      //根据传入的x值，求结果
      let resultY = a * pointXControl ** 3 + b * pointXControl ** 2 + c * pointXControl + d;
      return resultY;
    },

    //通过控制点坐标，求解上下曲线方程的参数
    calculateSplineParam(listSplinePoints){

      //公式 y = (x / c) ^  ** this.params.valueN1 * (1 - x / c) ** this.params.valueN2 * sum(i = 0,N)[ Ai * (x / c) ^ i]

      //公式进行求解系数
      let chordLength = this.params.chordLength;

      //当前采用的是五阶曲线进行方程组解析，用到六个控制点
      if(listSplinePoints.length < 6){
        return;
      }
      //方程组求解系数矩阵
      let matrix = [
        [listSplinePoints[0].x ** 5 / (chordLength ** 5),listSplinePoints[0].x ** 4 / (chordLength ** 4),listSplinePoints[0].x ** 3 / (chordLength ** 3),listSplinePoints[0].x ** 2 / (chordLength ** 2),listSplinePoints[0].x / chordLength,1],
        [listSplinePoints[1].x ** 5 / (chordLength ** 5),listSplinePoints[1].x ** 4 / (chordLength ** 4),listSplinePoints[1].x ** 3 / (chordLength ** 3),listSplinePoints[1].x ** 2 / (chordLength ** 2),listSplinePoints[1].x / chordLength,1],
        [listSplinePoints[2].x ** 5 / (chordLength ** 5),listSplinePoints[2].x ** 4 / (chordLength ** 4),listSplinePoints[2].x ** 3 / (chordLength ** 3),listSplinePoints[2].x ** 2 / (chordLength ** 2),listSplinePoints[2].x / chordLength,1],
        [listSplinePoints[3].x ** 5 / (chordLength ** 5),listSplinePoints[3].x ** 4 / (chordLength ** 4),listSplinePoints[3].x ** 3 / (chordLength ** 3),listSplinePoints[3].x ** 2 / (chordLength ** 2),listSplinePoints[3].x / chordLength,1],
        [listSplinePoints[4].x ** 5 / (chordLength ** 5),listSplinePoints[4].x ** 4 / (chordLength ** 4),listSplinePoints[4].x ** 3 / (chordLength ** 3),listSplinePoints[4].x ** 2 / (chordLength ** 2),listSplinePoints[4].x / chordLength,1],
        [listSplinePoints[5].x ** 5 / (chordLength ** 5),listSplinePoints[5].x ** 4 / (chordLength ** 4),listSplinePoints[5].x ** 3 / (chordLength ** 3),listSplinePoints[5].x ** 2 / (chordLength ** 2),listSplinePoints[5].x / chordLength,1]
      ];

      //矩阵求逆
      let invMatrix = this.inv(matrix);
      let trailingEdgeThickness = this.params.trailingEdgeThickness / 100 * chordLength;

      //对公式(x/c) ** this.params.valueN1 * (1-x/c) ** this.params.valueN2除到y值下
      let y0 = (listSplinePoints[0].y - listSplinePoints[0].x * trailingEdgeThickness / chordLength ** 2) / ((listSplinePoints[0].x / chordLength) ** this.params.valueN1 * (1 - listSplinePoints[0].x / chordLength) ** this.params.valueN2) / chordLength;
      let y1 = (listSplinePoints[1].y - listSplinePoints[1].x * trailingEdgeThickness / chordLength ** 2) / ((listSplinePoints[1].x / chordLength) ** this.params.valueN1 * (1 - listSplinePoints[1].x / chordLength) ** this.params.valueN2) / chordLength;
      let y2 = (listSplinePoints[2].y - listSplinePoints[2].x * trailingEdgeThickness / chordLength ** 2) / ((listSplinePoints[2].x / chordLength) ** this.params.valueN1 * (1 - listSplinePoints[2].x / chordLength) ** this.params.valueN2) / chordLength;
      let y3 = (listSplinePoints[3].y - listSplinePoints[3].x * trailingEdgeThickness / chordLength ** 2) / ((listSplinePoints[3].x / chordLength) ** this.params.valueN1 * (1 - listSplinePoints[3].x / chordLength) ** this.params.valueN2) / chordLength;
      let y4 = (listSplinePoints[4].y - listSplinePoints[4].x * trailingEdgeThickness / chordLength ** 2) / ((listSplinePoints[4].x / chordLength) ** this.params.valueN1 * (1 - listSplinePoints[4].x / chordLength) ** this.params.valueN2) / chordLength;
      let y5 = (listSplinePoints[5].y - listSplinePoints[5].x * trailingEdgeThickness / chordLength ** 2) / ((listSplinePoints[5].x / chordLength) ** this.params.valueN1 * (1 - listSplinePoints[5].x / chordLength) ** this.params.valueN2) / chordLength;

      //获取三次方程的abcdef参数
      let a = invMatrix[0][0] * y0 +
              invMatrix[0][1] * y1 +
              invMatrix[0][2] * y2 +
              invMatrix[0][3] * y3 +
              invMatrix[0][4] * y4 +
              invMatrix[0][5] * y5;
      let b = invMatrix[1][0] * y0 +
              invMatrix[1][1] * y1 +
              invMatrix[1][2] * y2 +
              invMatrix[1][3] * y3 +
              invMatrix[1][4] * y4 +
              invMatrix[1][5] * y5;
      let c = invMatrix[2][0] * y0 +
              invMatrix[2][1] * y1 +
              invMatrix[2][2] * y2 +
              invMatrix[2][3] * y3 +
              invMatrix[2][4] * y4 +
              invMatrix[2][5] * y5;
      let d = invMatrix[3][0] * y0 +
              invMatrix[3][1] * y1 +
              invMatrix[3][2] * y2 +
              invMatrix[3][3] * y3 +
              invMatrix[3][4] * y4 +
              invMatrix[3][5] * y5;
      let e = invMatrix[4][0] * y0 +
              invMatrix[4][1] * y1 +
              invMatrix[4][2] * y2 +
              invMatrix[4][3] * y3 +
              invMatrix[4][4] * y4 +
              invMatrix[4][5] * y5;
      let f = invMatrix[5][0] * y0 +
              invMatrix[5][1] * y1 +
              invMatrix[5][2] * y2 +
              invMatrix[5][3] * y3 +
              invMatrix[5][4] * y4 +
              invMatrix[5][5] * y5;

      //对获取到的系数进行公式整理

      // let x;
      // let y = c * (x / chordLength)  ** this.params.valueN1  * (1 - (x / chordLength)) ** this.params.valueN2 *
      //   (a * (x / chordLength) ** 5 + b * (x / chordLength) ** 4 + c * (x / chordLength) ** 3 + d * (x / chordLength) ** 2 + e * (x / chordLength) + f);

      //Todo 用来给产品公式创建曲线传入曲线方程用
      let formula = chordLength + " * ((x / " + chordLength + ") ^"+ this.params.valueN1 + "* (1 - (x / " + chordLength + ")^" + this.params.valueN2 + ") * ("
        + "(" + a + " * (x / " + chordLength + ") ^ 5) + "
        + "(" + b + " * (x / " + chordLength + ") ^ 4) + "
        + "(" + c + " * (x / " + chordLength + ") ^ 3) + "
        + "(" + d + " * (x / " + chordLength + ") ^ 2) + "
        + "(" + e + " * (x / " + chordLength + ")) + "
        + f + ")"
        + " + x * " + this.params.trailingEdgeThickness / (chordLength) ** 2 + ")";

      let formulaParam = {
        a : a,
        b : b,
        c : c,
        d : d,
        e : e,
        f : f,
        formula : formula,
      }
      return formulaParam;
    },

    //行列式
    det(square){
      //方阵约束
      if(square.length !== square[0].length){
        throw new Error();
      }
      //方阵结束
      let n = square.length;

      let result = 0;
      if(n > 3){
        for(let column = 0;column < n; column++){
          //去掉第0行第column列的矩阵
          let matrix = new Array(n - 1).fill(0).map(arr => new Array(n - 1).fill(0));
          for(let i = 0;i < n - 1;i++){
            for(let j = 0;j < n - 1;j++){
              if(j < column){
                matrix[i][j] = square[i + 1][j];
              }else{
                matrix[i][j] = square[i + 1][j + 1];
              }
            }
          }
          result += square[0][column] * Math.pow(-1,0 + column) * this.det(matrix);
        }
      }else if(n === 3){
        result = square[0][0] * square[1][1] * square[2][2] +
          square[0][1] * square[1][2] * square[2][0] +
          square[0][2] * square[1][0] * square[2][1] -
          square[0][2] * square[1][1] * square[2][0] -
          square[0][1] * square[1][0] * square[2][2] -
          square[0][0] * square[1][2] * square[2][1];
      }else if(n === 2){
        result = square[0][0] *square[1][1] - square[0][1] * square[1][0];
      }else if(n === 1){
        result = square[0][0];
      }
      return result;
    },
    //转置矩阵
    transpose(matrix){
      let result = new Array(matrix.length).fill(0).map(arr => new Array(matrix[0].length).fill(0));
      for(let i = 0;i < result.length;i++){
        for(let j = 0;j < result[0].length;j++){
          result[i][j] = matrix[j][i];
        }
      }
      return result;
    },
    //伴随矩阵
    adjoint(square){
      //方阵约束
      if(square.length !== square[0].length){
        throw new Error();
      }

      let n = square.length;
      let result = new Array(n).fill(0).map(arr => new Array(n).fill(0));
      for(let row = 0;row < n;row++){
        for(let column = 0;column < n;column++){
          //去掉第row行column列的矩阵
          let matrix = [];
          for(let i = 0;i < square.length;i++){
            if(i !== row){
              let arr = [];
              for(let j = 0;j < square.length;j++){
                if(j != column){
                  arr.push(square[i][j]);
                }
              }
              matrix.push(arr);
            }
          }
          result[row][column] = Math.pow(-1,row + column) * this.det(matrix);
        }
      }
      return this.transpose(result);
    },
    //逆矩阵
    inv(square){
      //方阵约束
      if(square.length !== square[0].length){
        throw new Error();
      }
      let detValue = this.det(square);
      let result = this.adjoint(square);
      for(let i = 0;i < result.length;i++){
        for(let j = 0;j < result.length;j++){
          result[i][j] /= detValue;
        }
      }
      return result;
    },
    //函数求导,在某点处的导数，采用0.00001步进方式进行导数求解
    getFunctionDerivativeOnX(formula,x){
      let chordLength = this.params.chordLength;

      let x1;
      let x2;
      if(x === 0){
        x1 = x;
        x2 = x + 0.00001;
      }else if(x === chordLength){
        x1 = x - 0.00001;
        x2 = x;
      }else{
        x1 = x - 0.00001;
        x2 = x + 0.00001;
      }

      // let y1 = chordLength  * (x1 / chordLength)  ** this.params.valueN1 * (1 - (x1 / chordLength)) ** this.params.valueN2 *
      //   (formula.a * (x1 / chordLength) ** 5 +
      //     formula.b * (x1 / chordLength) ** 4 +
      //     formula.c * (x1 / chordLength) ** 3 +
      //     formula.d * (x1 / chordLength) ** 2 +
      //     formula.e * (x1 / chordLength) +
      //     formula.f);
      // let y2 = chordLength  * (x2 / chordLength)  ** this.params.valueN1 * (1 - (x2 / chordLength)) ** this.params.valueN2 *
      //   (formula.a * (x2 / chordLength) ** 5 +
      //     formula.b * (x2 / chordLength) ** 4 +
      //     formula.c * (x2 / chordLength) ** 3 +
      //     formula.d * (x2 / chordLength) ** 2 +
      //     formula.e * (x2 / chordLength) +
      //     formula.f);
      let y1 = this.getYValueBySplineFunction(formula,x1 / chordLength);
      let y2 = this.getYValueBySplineFunction(formula,x2 / chordLength);

      return (y2 - y1) / (x2 - x1);
    },
    //获取函数在某点处的二次导数
    getFunctionTwoDerivativeOnX(formula,x){
      let x1;
      let x2;
      let dy1;    //y1处的导数
      let dy2;    //y2处的导数
      if(x !== 0){
        x1 = x - 0.00001;
        x2 = x + 0.00001;
      }else{
        x1 = x;
        x2 = x + 0.00001;
      }
      dy1 = this.getFunctionDerivativeOnX(formula,x1);
      dy2 = this.getFunctionDerivativeOnX(formula,x2);
      return (dy2 - dy1) / (x2 - x1);
    },
    //根据翼型曲线参数获取x对应的y值
    getYValueBySplineFunction(formulaParam,x){
      let chordLength = this.params.chordLength;
      let y = chordLength  * (x  ** this.params.valueN1 * (1 - x) ** this.params.valueN2 *
        (formulaParam.a * x ** 5 +
          formulaParam.b * x ** 4 +
          formulaParam.c * x ** 3 +
          formulaParam.d * x ** 2 +
          formulaParam.e * x +
          formulaParam.f) +
          x * this.params.trailingEdgeThickness / 100);
      return y;
    },

    //在拟合方程上进行取样点加密，使三阶样条曲线满足五阶翼型设计的需求,nInsertDir,0不加密，1左侧加密，2右侧加密,3双侧都加密
    calculateSplintPoints(formula,nNo,nInsertDir = 0){
      let chordLength = this.params.chordLength;
      let nStep = 1 / nNo;
      let nIndex = 1;
      let splinePoint = [];
      //起始点
      splinePoint.push(new THREE.Vector3(0,0));

      //左侧插值加密
      if(nInsertDir === 1 || nInsertDir === 3){
        //在原点位置，五次曲线转成三阶样条曲线的过程中因为样条线的二次曲率自动连续会导致x值为负的情况，
        // 因此在(0,step)区间上，需要进行采样点加密，且加密次序在(0,step)上越趋近于0越密集，现有方案是在(0,step)上加密点为step/2、step/4、step/8、step/16、step/32、step/64、step/128...直到低至精度0.00001
        let dNeedPrecisionS = 0.00001;
        let listInsertPointsStart = [];
        let dSamplePrecisionXS = nStep / 2;    //初始采样步长为原步长的一半

        let yInsertStart;
        while(dNeedPrecisionS <= dSamplePrecisionXS){
          yInsertStart = this.getYValueBySplineFunction(formula,dSamplePrecisionXS);
          listInsertPointsStart.push(new THREE.Vector3(dSamplePrecisionXS * chordLength,yInsertStart));
          dSamplePrecisionXS /= 2;
        }

        //将(0,step)插值样点倒序添加到主插值列表中
        for(let n = listInsertPointsStart.length - 1;n >= 0;n--){
          splinePoint.push(listInsertPointsStart[n]);
        }
      }

      while(nIndex < nNo){
        // let y = chordLength  * (nIndex * nStep)  ** this.params.valueN1 * (1 - (nIndex * nStep)) ** this.params.valueN2 *
        //   (formula.a * (nIndex * nStep) ** 5 +
        //     formula.b * (nIndex * nStep) ** 4 +
        //     formula.c * (nIndex * nStep) ** 3 +
        //     formula.d * (nIndex * nStep) ** 2 +
        //     formula.e * (nIndex * nStep) +
        //     formula.f);
        let y = this.getYValueBySplineFunction(formula,nIndex * nStep);
        splinePoint.push(new THREE.Vector3(nIndex * nStep * chordLength,y));
        nIndex++;
      }

      //右侧插值加密
      if(nInsertDir === 2 || nInsertDir === 3){
        //在弦长点位置，五次曲线转成三阶样条曲线的过程中因为样条线的二次曲率自动连续可能会导致x值超限的情况，
        // 因此在((nNo-1)*step,1)区间上，需要进行采样点加密，且加密次序在((nNo-1)*step,1)上越趋近于1越密集，现有方案是在((nNo-1)*step,1)上加密点为(nNo-1)*step + step/2、(nNo-1)*step + step/4、(nNo-1)*step + step/8...直到至精度0.00001
        let dNeedPrecisionE = 0.00001;
        let listInsertPointsEnd = [];
        let dSamplePrecisionXE = nStep / 2;    //初始采样步长为原步长的一半
        let dLastPoints = (nNo-1)*nStep;      //上次的X值

        let yInsertEnd;
        while(dLastPoints + dSamplePrecisionXE <= 1 - dNeedPrecisionE){
          yInsertEnd = this.getYValueBySplineFunction(formula,dLastPoints + dSamplePrecisionXE);
          listInsertPointsEnd.push(new THREE.Vector3((dLastPoints + dSamplePrecisionXE) * chordLength,yInsertEnd));
          dLastPoints += dSamplePrecisionXE;
          dSamplePrecisionXE /= 2;
        }

        //将(0,step)插值样点倒序添加到主插值列表中
        for(let n = 0;n <= listInsertPointsEnd.length - 1;n++){
          splinePoint.push(listInsertPointsEnd[n]);
        }
      }

      //终止点
      splinePoint.push(new THREE.Vector3(chordLength,this.params.trailingEdgeThickness / 100 * chordLength));
      return splinePoint;
    },

    getIntersectUpAndDownSpline(formulaUp,formulaDown){
      let x = 0;
      let yUp = 0;
      let yDown = 0;
      while(x < 1){
        yUp = this.getYValueBySplineFunction(formulaUp,x);
        yDown = this.getYValueBySplineFunction(formulaDown,x);
        if(yUp + 0.0000001 < yDown){
         return true;
        }
        x += 0.01;
      }
      return false;
    },

    showMessage(message){
      wingProfileDesignCommand.eventBusAlertMsg(message,"warning");
    },
    console(point){
        let x = point.x;
        let y = point.y;
        console.log({x,y});
    },

    //批量点进行旋转平移
    transformPoints(vectorPoints,rotateAngle,vectorOffset,vectorRotatePoint){
      let vectorPointsNew = [];
      vectorPoints.forEach(vectorPoint => {
          vectorPointsNew.push(this.transformPoint(vectorPoint,rotateAngle,vectorOffset,vectorRotatePoint));
      })
      return vectorPointsNew;
    },
    transformPoint(vectorPoint,rotateAngle,vectorOffset,vectorRotatePoint){
      //先移到原点处，再进行旋转
      let vectorPointNew = new THREE.Vector3(vectorPoint.x - vectorRotatePoint.x,vectorPoint.y - vectorRotatePoint.y);
      //旋转
      if(rotateAngle !== 0){
        vectorPointNew.x = Math.cos(rotateAngle) * vectorPoint.x - Math.sin(rotateAngle) * vectorPoint.y;
        vectorPointNew.y = Math.sin(rotateAngle) * vectorPoint.x + Math.cos(rotateAngle) * vectorPoint.y;
      }
      //从原点旋转完后再移回原位置
      vectorPointNew.x += vectorRotatePoint.x;
      vectorPointNew.y += vectorRotatePoint.y;

      //进行平移
      if(vectorOffset.x !== 0 || vectorOffset.y !== 0){
        vectorPointNew.x += vectorOffset.x;
        vectorPointNew.y += vectorOffset.y;
      }
      return vectorPointNew;
    },
    //判断NACA四位数是否合理
    judgeNACAValueValid(strNACA){
      if(strNACA.length !== 4){
        return false;
      }

      let n = Number(strNACA);
      if(isNaN(n)){
        return false;
      }
      return true;
    },
    getNACANumbers(strNACA){
      //截取第一、二、三四位分别为相对弯度、相对弯度位置、最大相对厚度
      let strMaxThickness = strNACA.substring(0,1);
      let strMaxCamberPos = strNACA.substring(1,2);
      let strMaxCamber = strNACA.substring(2);
      let params = [Number(strMaxThickness),Number(strMaxCamberPos),Number(strMaxCamber)];
      return params;
    },

    //清理列表
    setAllParam() {
      this.drawPointsUp = [];
      this.drawPointsDown = [];
      this.samplePointsUp = [];
      this.samplePointsDown = [];
      this.listControlPointsUp = [];
      this.listControlPointsDown = [];
      this.formulaParamUp = [];
      this.formulaParamDown = [];
    },
  },

  watch: {
    "params.chordLength": function () {
      this.preview();
    },
    "params.maxThickness": function () {
      this.preview();
    },
    "params.valueN1": function () {
      this.preview();
    },
    "params.valueN2": function () {
      this.preview();
    },
    "params.maxCamber": function () {
      //只有当非对称曲线状态下，才能刷新
      if(!this.params.checkedSymmetry){
        this.preview();
      }
    },
    "params.maxCamberPos": function () {
      this.preview();
    },
    "params.rotateAngle": function () {
      this.preview();
    },
    "params.dirUOffset": function () {
      this.preview();
    },
    "params.dirVOffset": function () {
      this.preview();
    },
    "params.trailingEdgeThickness": function () {
      //只有当非对称曲线状态下，才能刷新
      if(!this.params.checkedSymmetry){
        this.preview();
      }
    },
    "params.checkedSymmetry": function () {
      //如果是对称翼型，则最大弯度、后缘厚度强制设置为零
      if(this.params.checkedSymmetry){
        //将原来参数进行记录
        this.params.lastMaxCamber = this.params.maxCamber;
        this.params.lastMaxCamberPos = this.params.maxCamberPos;
        this.params.lastTrailingEdgeThickness = this.params.trailingEdgeThickness;
        this.params.lastRotateAngle = this.params.rotateAngle;

        //将参数进行清零
        this.params.trailingEdgeThickness = 0;
        this.params.maxCamberPos = 0;
        this.params.maxCamber = 0;
      }else{
        //将原来参数进行记录
        this.params.maxCamber = this.params.lastMaxCamber;
        this.params.maxCamberPos = this.params.lastMaxCamberPos;
        this.params.trailingEdgeThickness = this.params.lastTrailingEdgeThickness;
        this.params.rotateAngle = this.params.lastRotateAngle;
      }

      this.preview();
    },
    "params.curTab": function(){
      this.preview();
      if(this.params.curTab === "TabParam"){
        this.params.trailingEdgeThickness = this.params.lastTrailingEdgeThickness;
      }else if(this.params.curTab === "TabNACA"){
        this.params.lastTrailingEdgeThickness = this.params.trailingEdgeThickness;
      }

    },
    "params.standardNACAFour": function(){
      this.preview();
    },
  },

};
</script>
<style scoped>
.wrap {
    font-size: 12px;
}
.query-description {
    height: 24px;
    font-size: 12px;
    line-height: 24px;
    display: flex;
    justify-content: space-between;
    margin-left: 10px;
    margin-top: 3px;
}
/*.label {*/
/*    max-width: 100px;*/
/*    overflow: hidden;*/
/*    text-overflow: ellipsis;*/
/*}*/
.result-list {
    padding: 2px 10px;
    max-height: 180px;
    overflow-y: auto;
}

.result-item {
    margin: 1px 0;
    padding: 2px 0 2px 4px;
    display: flex;
    align-items: center;
}

.result-item .angle-icon {
    font-weight: bold;
    padding: 0 2px;
}

.result-item-title {
    flex-grow: 1;
}

.ident {
    padding-left: 14px;
}

.selected-query-li {
    border-radius: 3px;
    background-color: var(--bg-color3);
}
</style>
